package org.ecype.diego;

import android.text.TextUtils;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import androidx.persistence.EntityNotFoundException;
import androidx.persistence.PersistenceException;

public class Contract implements Iterable<Entity> {

	private String authority;
	private Set<Entity> entities;
	private String dbFileName;
	private int version;

	public Contract(Class<?> contract) throws IllegalAccessException {
		androidx.persistence.Contract annotation = contract.getAnnotation(androidx.persistence.Contract.class);
		if (annotation == null) {
			throw new PersistenceException("Missed annotation @Contart(authority)");
		}
		authority = annotation.authority();
		dbFileName = annotation.dbFileName();
		version = annotation.version();
		if (TextUtils.isEmpty(dbFileName)) {
		    dbFileName = null;
		}
		entities = new HashSet<Entity>();
		for (Class<?> entity : Annotations.extractAnnotatedClasses(contract, androidx.persistence.Entity.class)) {
			entities.add(new Entity(this, entity));
		}

		for (Entity entity : entities) {
			entity.setupReferences();
		}

	}

	public String getAuthority() {
		return authority;
	}

	public Set<Entity> getEntries() {
		return entities;
	}

	public String getDbFileName() {
        return dbFileName;
    }
	
	public int getVersion() {
        return version;
    }
	
	public Iterator<Entity> iterator() {
		return new Iterator<Entity>() {

			private Set<Entity> nextEntities = new HashSet<Entity>(entities);
			private Set<Entity> previousEntities = new HashSet<Entity>();
			private Entity next;

			@Override
			public boolean hasNext() {
				for (Entity entity : nextEntities) {
					Set<Entity> references = entity.getReferenceEntities();
					if (references.isEmpty() || previousEntities.containsAll(references)) {
						nextEntities.remove(entity);
						previousEntities.add(entity);
						next = entity;
						return true;
					}
				}
				return false;
			}

			@Override
			public Entity next() {
				return next;
			}

			@Override
			public void remove() {
				throw new UnsupportedOperationException();
			}

		};
	}

	public Entity findByName(String reference) {
		for (Entity entity : entities) {
			if (entity.getName().equals(reference)) {
				return entity;
			}
		}
		throw new EntityNotFoundException(reference);
	}

	public Entity findByClass(Class<?> entityClass) {
		for (Entity entity : entities) {
			if (entity.getEntryClass().equals(entityClass)) {
				return entity;
			}
		}
		throw new EntityNotFoundException(entityClass.getName());
	}

}
